package com.csx.governance.traffic.condition.support;

import com.csx.governance.traffic.condition.ExpressionExecutor;
import com.csx.governance.traffic.condition.ParsedExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.Map;
import java.util.Objects;

/**
 * @author cuisongxu
 * @date 2023/2/25 12:07
 */
public class SpringExpressionExecutor implements ExpressionExecutor {

    private static final Logger logger = LoggerFactory.getLogger(SpringExpressionExecutor.class);

    private final ExpressionParser parser;

    private final EvaluationContext evaluationContext;

    private final ParserContext parserContext;

    public SpringExpressionExecutor() {
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.addPropertyAccessor(new MapAccessor());
        this.parser = new SpelExpressionParser();
        this.evaluationContext = standardEvaluationContext;
        this.parserContext = new TemplateParserContext();
    }

    public SpringExpressionExecutor(ExpressionParser parser) {
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.addPropertyAccessor(new MapAccessor());
        this.parser = parser;
        this.evaluationContext = standardEvaluationContext;
        this.parserContext = new TemplateParserContext();
    }

    public SpringExpressionExecutor(ExpressionParser parser, EvaluationContext evaluationContext, ParserContext parserContext) {
        this.parser = parser;
        this.evaluationContext = evaluationContext;
        this.parserContext = parserContext;
    }

    public SpringExpressionExecutor(ExpressionParser parser, EvaluationContext evaluationContext) {
        this(parser, evaluationContext, new TemplateParserContext());
    }

    @Override
    public ParsedExpression parse(String expressionStatement) {
        Expression expression = parser.parseExpression("#{" + expressionStatement + "}", parserContext);
        return new ParsedExpression(expression);
    }

    @Override
    public <T> T execute(ParsedExpression parsedExpression, Map<String, Object> params, Class<T> desiredResultType) {
        Expression originParsedExpression = parsedExpression.getExpression(Expression.class);
        Objects.requireNonNull(originParsedExpression, "Expression cannot be null");
        logger.info("Execute expression:{}, params:{}", originParsedExpression.getExpressionString(), params);
        return originParsedExpression.getValue(evaluationContext, params, desiredResultType);
    }

}
